package io.gitee.hejiang.action.impl;

import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;

import io.gitee.hejiang.action.SwaggerSqlAction;
import io.gitee.hejiang.action.SwaggerSqlActionProvider;
import io.swagger.inflector.models.RequestContext;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;

public class CacheActionProvider implements SwaggerSqlActionProvider {
	private static Logger _logger = LoggerFactory.getLogger(CacheAction.class);
	private static ObjectMapper _mapper = new ObjectMapper();

	@Override
	public SwaggerSqlAction createAction(Map<String, Object> config) {
		String type = (String) config.get("type");
		if ("cache".equalsIgnoreCase(type) || config.containsKey("cache") || config.containsKey("evict")) {
			return new CacheAction();
		}
		return null;
	}

	static class CacheAction implements SwaggerSqlAction {
		final static String _separator = "###";

		SwaggerSqlAction _next;
		String _cache;
		List<String> _evict;

		@SuppressWarnings("unchecked")
		@Override
		public void init(Map<String, Object> config, Map<String, JavaType> argumentTypes, SwaggerSqlAction next)
				throws Exception {
			_next = next;
			_cache = (String) config.get("cache");
			if (_cache != null) {
				_cache = _cache + _separator;
			}

			Object evict = config.get("evict");
			if (evict == null) {
			} else if (evict instanceof String) {
				_evict = new ArrayList<>();
				_evict.add(((String) evict) + _separator);
			} else if (evict instanceof List) {
				_evict = new ArrayList<>();
				for (String name : (List<String>) evict) {
					_evict.add(name + _separator);
				}
			} else {
				throw new Exception("Fail to init(invalid evict)");
			}
		}

		@Override
		public Object execute(RequestContext request, Connection connection, Map<String, Object> arguments,
				Map<String, Object> context) throws Exception {
			final CacheManager cm = CacheManager.getCacheManager(null);
			final Cache cache = cm.getCache("swagger-sql");

			// 驱逐缓存
			if (_evict != null) {
				for (Object key : cache.getKeysNoDuplicateCheck()) {
					for (String prefix : _evict) {
						if (((String) key).startsWith(prefix)) {
							cache.remove(key);
							_logger.info("Evicted:   {}", key);
							break;
						}
					}
				}
			}

			String key = null;
			// 检查缓存
			if (_cache != null) {
				key = _cache + _mapper.writeValueAsString(arguments);
				Element el = cache.get(key);
				if (el != null) {
					_logger.info("Hit cache: {}", key);
					return el.getObjectValue();
				} else {
					_logger.info("Noncached: {}", key);
				}
			}

			// 链式执行
			Object ret = null;
			if (_next != null) {
				ret = _next.execute(request, connection, arguments, context);
				if (_cache != null) {
					cache.put(new Element(key, ret));
				}
			}

			return ret;
		}
	}
}
